﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Zo.Queues;

namespace Zo.Tasks
{
    /// <summary>
    /// 线程安全的任务。
    /// </summary>
    /// <remarks>
    /// 每个任务以队列的形式处理一批数据。
    /// </remarks>
    public interface IMonoConcurrentTask
    {
        /// <summary>
        /// 当前任务状态。
        /// </summary>
        /// <remarks>
        /// 0：空闲。
        /// 1：忙碌。
        /// 2：退出。
        /// </remarks>
        MonoConcurrentTaskState State { get; }

        /// <summary>
        /// 是否在执行完后自动释放资源。
        /// </summary>
        /// <remarks>
        /// 是 True：Schedule() 执行结束时，自动将 _status 设置成“Idle（空闲）”。
        /// 是 False：Schedule() 执行结束时，不会将 _status 设置成“Idle（空闲）”，需要手动调用 End() 方法。
        /// </remarks>
        bool IsAutoEnd { get; set; }

        /// <summary>
        /// 执行任务。
        /// </summary>
        /// <param name="task">任务信息。</param>
        void Handler(object task);

        /// <summary>
        /// 任务结束（状态由“忙碌”设置为“空闲”）。
        /// </summary>
        void End();
    }

    /// <summary>
    /// 线程安全的任务。
    /// </summary>
    public interface IMonoConcurrentTask<in TSource> : IMonoConcurrentTask
    {
        /// <summary>
        /// 开始处理。
        /// </summary>
        /// <param name="task">任务信息。</param>
        void Handler(TSource task);
    }

    /// <summary>
    /// 线程安全的任务基类。
    /// </summary>
    public abstract class MonoConcurrentTaskBase : IMonoConcurrentTask
    {
        #region 字段

        /// <summary>
        /// 状态。
        /// </summary>
        private int _status = TaskStatus.Idle;

        /// <summary>
        /// 任务队列。
        /// </summary>
        private readonly MonoConcurrentQueue<object> _waitQueues = new MonoConcurrentQueue<object>();

        #endregion 字段

        #region 属性

        /// <summary>
        /// 当前任务状态。
        /// </summary>
        /// <remarks>
        /// 0：空闲。
        /// 1：忙碌。
        /// 2：退出。
        /// </remarks>
        public MonoConcurrentTaskState State
        {
            get
            {
                switch (_status)
                {
                    case TaskStatus.Idle:
                        return MonoConcurrentTaskState.Idle;

                    case TaskStatus.Busy:
                        return MonoConcurrentTaskState.Busy;

                    case TaskStatus.Exit:
                        return MonoConcurrentTaskState.Exit;

                    default:
                        return MonoConcurrentTaskState.Exit;
                }
            }
        }

        /// <summary>
        /// 是否在执行完后自动释放资源。
        /// </summary>
        /// <remarks>
        /// 是 True：Schedule() 执行结束时，自动将 _status 设置成“Idle（空闲）”。
        /// 是 False：Schedule() 执行结束时，不会将 _status 设置成“Idle（空闲）”，需要手动调用 End() 方法。
        /// </remarks>
        public bool IsAutoEnd { get; set; } = true;

        /// <summary>
        /// 每次任务的执行时间。
        /// </summary>
        public DateTime ExecuteTime { get; protected set; }

        #endregion 属性

        #region 执行

        /// <summary>
        /// 执行任务。
        /// </summary>
        /// <param name="task">任务信息。</param>
        public virtual void Handler(object task)
        {
            _waitQueues.Enqueue(task);

            Schedule();
        }

        #endregion 执行

        #region 任务调度

        /// <summary>
        /// 任务调度。
        /// </summary>
        private void Schedule()
        {
            if (_status == TaskStatus.Exit) return;

            if (_waitQueues.Count <= 0)
                return;

            OnExecuteingSchedule();

            //判断 _status 的状态是否是 ConnectionStatus.Idle，如果是，则替换成 ConnectionStatus.Busy。
            var status = Interlocked.CompareExchange(ref _status, TaskStatus.Busy, TaskStatus.Idle);

            OnExecutedSchedule();

            if (status == TaskStatus.Idle)
            {
                //将消息放入微软提供的线程池。
                ThreadTask.Current.AddTask(Execute, null);
            }
        }

        /// <summary>
        /// 线程池处理。
        /// </summary>
        private void Execute(object state)
        {
            if (_waitQueues.Count <= 0)
                return;

            object currentTask;

            //取出消息。
            var status = _waitQueues.TryDequeue(out currentTask);

            if (!status)
            {
                return;
            }

            OnExecute(currentTask);

            if (IsAutoEnd)
                OnEnd();
        }

        #endregion 任务调度

        #region 任务结束

        /// <summary>
        /// 任务结束（状态由“忙碌”设置为“空闲”）。
        /// </summary>
        public void End()
        {
            if (!IsAutoEnd)
                OnEnd();
        }

        /// <summary>
        /// 任务结束。
        /// </summary>
        protected virtual void OnEnd()
        {
            //Interlocked.CompareExchange(ref _status, TaskStatus.Idle, TaskStatus.Busy);

            OnExecuteingEnd();

            //将状态由“忙碌”切换成“空闲”。
            Thread.VolatileWrite(ref _status, TaskStatus.Idle);

            OnExecutedEnd();

            if (_waitQueues.Count > 0)
            {
                Schedule();
            }
        }

        #endregion 任务结束

        /// <summary>
        /// 任务调度开始前。
        /// </summary>
        protected virtual void OnExecuteingSchedule() { }

        /// <summary>
        /// 任务调度开始后。
        /// </summary>
        protected virtual void OnExecutedSchedule() { }

        /// <summary>
        /// 任务结束前。
        /// </summary>
        protected virtual void OnExecuteingEnd() { }

        /// <summary>
        /// 任务结束后。
        /// </summary>
        protected virtual void OnExecutedEnd() { }

        /// <summary>
        /// 处理任务。
        /// </summary>
        /// <param name="currentTask"></param>
        protected abstract void OnExecute(object currentTask);

        #region 获得队列集合

        /// <summary>
        /// 获得数据队列中的数据集合。
        /// </summary>
        /// <returns>数据集合。</returns>
        public IEnumerable<object> FindDataToList()
        {
            return _waitQueues.ToArray();
        }

        #endregion 获得队列集合

        #region private class

        /// <summary>
        /// 连接状态。
        /// </summary>
        protected struct TaskStatus
        {
            /// <summary>
            /// 空闲。
            /// </summary>
            public const int Idle = 0;

            /// <summary>
            /// 忙碌。
            /// </summary>
            public const int Busy = 1;

            /// <summary>
            /// 退出。
            /// </summary>
            public const int Exit = 2;
        }

        #endregion private class
    }

    /// <summary>
    /// 线程安全的任务基类。
    /// </summary>
    /// <typeparam name="TSource">数据类型。</typeparam>
    public abstract class MonoConcurrentTaskBase<TSource> : MonoConcurrentTaskBase, IMonoConcurrentTask<TSource>
    {
        /// <summary>
        /// 开始处理。
        /// </summary>
        /// <param name="task">任务信息。</param>
        public virtual void Handler(TSource task)
        {
            base.Handler(task);
        }

        /// <summary>
        /// 执行任务。
        /// </summary>
        /// <param name="currentTask">任务信息。</param>
        protected override void OnExecute(object currentTask)
        {
            OnHandler((TSource)currentTask);
        }

        /// <summary>
        /// 处理任务。
        /// </summary>
        /// <param name="currentTask">任务信息。</param>
        protected abstract void OnHandler(TSource currentTask);

        #region 获得队列集合

        /// <summary>
        /// 获得数据队列中的数据集合。
        /// </summary>
        /// <returns>数据集合。</returns>
        public new IEnumerable<TSource> FindDataToList()
        {
            return base.FindDataToList().Cast<TSource>();
        }

        #endregion 获得队列集合
    }

    /// <summary>
    /// 任务状态。
    /// </summary>
    public enum MonoConcurrentTaskState
    {
        /// <summary>
        /// 空闲。
        /// </summary>
        Idle = 0,

        /// <summary>
        /// 忙碌。
        /// </summary>
        Busy = 1,

        /// <summary>
        /// 退出。
        /// </summary>
        Exit = 2,
    }
}